package com.lzwork.gpt.utility.batchjob;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import com.alibaba.fastjson.JSONObject;
import com.lzwork.utility.data.TypeEnum;
import com.lzwork.utility.dictionary.MapDictionary;
import com.lzwork.utility.file.FileUtility;
import com.lzwork.utility.file.LineContent;
import com.lzwork.utility.logger.LOGGER;
import com.lzwork.utility.str.StrUtility;
import com.lzwork.web.data.BoolResultInfo;
import com.lzwork.web.data.CallResultInfo;

import lombok.Getter;
import lombok.NonNull;

/**
 * @author LiZheng
 * @date 2024年7月30日 10:30:59
 */

public abstract class BatchJobManager
{
	protected final static String lock = "";

	protected final static String TEMP_FOLDER = "__temp";

	public static long getTick()
	{
		long tick;
		synchronized (lock)
		{
			tick = System.currentTimeMillis();
			try
			{
				Thread.sleep(10);
			} catch (InterruptedException e)
			{
			}
		}

		return tick;
	}

	protected BatchJobManager()
	{
		root = getRootFolder();
		if (!root.exists())
			root.mkdirs();
	}

	protected abstract File getRootFolder();

	public abstract List<TypeEnum> getAllJobTypes();

	private File root;

	protected Map<String, SingleJob> jobMap = new HashMap<>();
	protected Map<String, SingleJob> titleMap = new HashMap<>();
	protected MapDictionary<String, SingleJob> userMap = new MapDictionary<>();

	public File root()
	{
		return root;
	}

	public File tempFolder()
	{
		File folder = FileUtility.getFileSafe(root, TEMP_FOLDER);
		if (!folder.exists())
			folder.mkdirs();
		return folder;
	}

	public abstract void refreshState();

	public boolean hasUser(String username)
	{
		return userMap.containsKey(username);
	}

	public abstract CallResultInfo<? extends SingleJob> doCreateJob(File file, @NonNull TypeEnum type, String owner, long tick,
			String rename);

	public BoolResultInfo createJob(File file, TypeEnum type, String owner, long tick, String rename)
	{
		if (type == null)
			return BoolResultInfo.makeFalse("Missing job type.");

		CallResultInfo<? extends SingleJob> cr = doCreateJob(file, type, owner, tick, rename);

		if (cr.target() == null)
			return BoolResultInfo.makeFalse(cr.info());

		SingleJob job = cr.target();
		addJob(job);

		JSONObject result = new JSONObject();
		result.put("id", job.getJobId());
		result.put("type", type.getName());
		result.put("username", owner);

		return BoolResultInfo.makeTrue(result);
	}

	protected void addJob(SingleJob job)
	{
		String id = job.getJobId();
		jobMap.put(id, job);
		titleMap.put(job.getJobTitle(), job);
		userMap.pick(job.getUser()).put(id, job);
	}

	@Getter
	public class LocalFileInfo extends CallResultInfo<File>
	{
		LocalFileInfo(Object info)
		{
			super(null, info);
		}

		LocalFileInfo(File target, Object info, String id, String title)
		{
			super(target, info);
			this.id = id;
			this.title = title;
		}

		public boolean isDone()
		{
			return target != null;
		}

		String id;
		String title;
	}

	public LocalFileInfo saveInputFile(File input, String rename, TypeEnum type)
	{
		if (!input.exists())
			return new LocalFileInfo("Input File not found.");

		String name = input.getName();
		int split = name.indexOf('@');
		if (split < 0)
			return new LocalFileInfo("Invalid file name format.");

		String filename = name.substring(split + 1);
		String jobName = name.substring(0, split);
		String folderName = "";

		if (!StrUtility.stringValid(rename))
			rename = jobName.substring(0, 8) + "-" + type.getName();
		else
			folderName = jobName + "-";

		String jobTitle = rename;
		int suffix = 1;
		while (titleMap.containsKey(jobTitle))
		{
			jobTitle = rename + " (" + suffix + ")";
			suffix++;
		}

		folderName += jobTitle;

		File folder = FileUtility.getFileSafe(root(), folderName + "/");
		folder.mkdirs();

		File target = FileUtility.getFileSafe(folder, filename);
		input.renameTo(target);

		return new LocalFileInfo(target, "", jobName, jobTitle);
	}

	/**
	 * @param sortType
	 *            0-按名称 1-按状态 2-添加日期 3-场景类型
	 * @return sorted list without rewrite origin list
	 */
	public List<SingleJob> getAllJob(int sortType, String owner)
	{
		checkDummys();
		List<SingleJob> result = new ArrayList<>();

		if (StrUtility.stringValid(owner))
		{
			Map<String, SingleJob> map = userMap.get(owner);
			if (map != null)
				result.addAll(map.values());
		} else
			result.addAll(jobMap.values());

		boolean increase = (sortType / 10) == 0;
		int itemType = sortType % 10;

		if (increase)
			result.sort((o1, o2) -> (o1.compareTo(o2, itemType)));
		else
			result.sort((o1, o2) -> (o2.compareTo(o1, itemType)));

		return result;
	}

	public SingleJob getJob(String id)
	{
		return jobMap.get(id);
	}

	Set<SingleJob> dummys = new HashSet<>();

	protected int checkDummys()
	{
		int total = dummys.size();
		if (total == 0)
			return 0;

		int del = 0;
		Iterator<SingleJob> it = dummys.iterator();

		while (it.hasNext())
		{
			SingleJob job = it.next();

			if (job == null)
			{
				it.remove();
				del++;
			} else if (!job.isRunning())
			{
				BoolResultInfo delDone = SingleJob.deleteJobFiles(job.getJobFolder());
				if (delDone.isDone())
				{
					LOGGER.log(LOGGER.FILE, "Delayed delete job folder [" + job.jobId + "] after job thread stop.");
					it.remove();
					del++;
				} else
					LOGGER.log(LOGGER.FILE, "Delayed delete job folder [" + job.jobId + "] fail for files: " + delDone.info());
			}
		}

		LOGGER.log(LOGGER.FILE, "Delete " + del + "/" + total + " dummy jobs.");

		return del;
	}

	public BoolResultInfo delJob(String id)
	{
		SingleJob job = jobMap.get(id);
		if (job == null)
			return BoolResultInfo.makeFalse("任务 [" + id + "] 不存在");
		LineContent.saveFile(job.dummyMark(), "");

		boolean isRunning = job.stopJob(true);

		if (isRunning)
			dummys.add(job);
		else
		{
			SingleJob.deleteJobFiles(job.getJobFolder());
			LOGGER.log(LOGGER.FILE, "Delete job folder [" + job.jobId + "]");
		}

		String owner = job.getUser();
		String title = job.getJobTitle();

		jobMap.remove(id);
		userMap.deleteItem(owner, id, true);
		titleMap.remove(title);

		return BoolResultInfo.makeTrue("");
	}

	public void onJobForceStop(SingleJob job)
	{
		SingleJob.deleteJobFiles(job.getJobFolder());
	}

	public BoolResultInfo resetJob(String id)
	{
		SingleJob job = jobMap.get(id);
		if (job == null)
			return BoolResultInfo.makeFalse("任务 [" + id + "] 不存在");

		return job.reset();
	}

	public BoolResultInfo beginJob(String id)
	{
		SingleJob job = jobMap.get(id);
		if (job == null)
			return BoolResultInfo.makeFalse("任务 [" + id + "] 不存在");

		return job.doJob();
	}

	public abstract TypeEnum parseType(int type);
}
